﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Engine;
using Game;
using GameEntitySystem;
using Microsoft.VisualBasic;
using TemplatesDatabase;

namespace MFTrain
{
    public class ComponentMFTrain : Component, IUpdateable
    {
        private static readonly Vector3 center = new Vector3(0.5f, 0, 0.5f);
        private static readonly Quaternion[] directions =
        {
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, 0),
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, MathUtils.PI * 0.5f),
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, MathUtils.PI),
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, -MathUtils.PI * 0.5f)
        };
        private static readonly Quaternion upwardDirection = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), MathUtils.PI * 0.25f);
        private static readonly Quaternion downwardDirection = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), MathUtils.PI * -0.25f);
        private static readonly Vector3[] forwardVectors =
        {
            new Vector3(0, 0, -1),
            new Vector3(-1, 0, 0),
            new Vector3(0, 0, 1),
            new Vector3(1, 0, 0)
        };

        internal ComponentBody m_componentBody;
        public ComponentMFTrain ParentBody;
        protected ComponentDamage componentDamage;
        protected float m_outOfMountTime;
        private ComponentMount m_componentMount;
        private SubsystemBodies m_systemBodies;
        private SubsystemTerrain m_systemTerrain;
        private SubsystemPickables SubsystemPickables;

        //子实体组件
        public ComponentMFEngine componentEngine;
        public ComponentMFChestCache componentChestCache;//缓存物品用的箱子

        private int m_forwardDirection;
        private Quaternion rotation;
        private Vector3 forwardVector;
        private bool isRunning => m_componentBody?.Velocity.Length() > 4;

        public int Direction
        {
            get { return m_forwardDirection; }
            set
            {
                forwardVector = forwardVectors[value];
                m_forwardDirection = value;
                rotation = directions[value];
            }
        }

        public UpdateOrder UpdateOrder => UpdateOrder.Default;

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap)
        {
            base.Load(valuesDictionary, idToEntityMap);
            m_componentBody = Entity.FindComponent<ComponentBody>(true);
            componentDamage = Entity.FindComponent<ComponentDamage>();
            m_componentMount = Entity.FindComponent<ComponentMount>(true);
            m_systemBodies = Project.FindSubsystem<SubsystemBodies>();
            m_systemTerrain = Project.FindSubsystem<SubsystemTerrain>();
            SubsystemPickables = Project.FindSubsystem<SubsystemPickables>();

            if ((componentEngine = Entity.FindComponent<ComponentMFEngine>()) != null)//获取火车引擎
            {
                m_componentBody.CollidedWithBody += CollidedWithBody;
            }
            if ((componentChestCache = Entity.FindComponent<ComponentMFChestCache>()) != null)//获取编写便携合成
            {
                //留着以后加逻辑
            }
            //else
            //    ParentBody = valuesDictionary.GetValue("ParentBody", default(EntityReference)).GetComponent<ComponentMFTrain>(Entity, idToEntityMap, false);

            Direction = valuesDictionary.GetValue("Direction", 0);
        }

        public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap)
        {
            if (m_forwardDirection != 0)
                valuesDictionary.SetValue("Direction", m_forwardDirection);
            var value = EntityReference.FromId(ParentBody, entityToIdMap);
            if (!value.IsNullOrEmpty())
                valuesDictionary.SetValue("ParentBody", value);
        }

        /// <summary>
        /// 与生物碰撞时执行的逻辑
        /// </summary>
        /// <param name="body"></param>
        public void CollidedWithBody(ComponentBody body)
        {
            if (isRunning)
            {
                if (MathUtils.Abs(body.Density - 9.76f) <= float.Epsilon)
                    return;
                Vector2 v = m_componentBody.Velocity.XZ - body.Velocity.XZ;
                float amount = v.LengthSquared() * .3f;
                if (amount < .02f || m_componentBody.Velocity.XZ.LengthSquared() == 0f) return;
                var health = body.Entity.FindComponent<ComponentHealth>();
                if (health != null)
                    health.Injure(amount / health.AttackResilience, null, false, "被火车创死啦!");
                else
                    body.Entity.FindComponent<ComponentDamage>()?.Damage(amount);
                body.ApplyImpulse(MathUtils.Clamp(1.25f * 6f * MathUtils.Pow(m_componentBody.Mass / body.Mass, 0.33f), 0f, 6f) * Vector3.Normalize(body.Position - m_componentBody.Position));

                //爆炸
                if (body.Entity.FindComponent<ComponentMFEngine>() != null)
                {
                    ThrowAllItems();
                    Project.FindSubsystem<SubsystemExplosions>().AddExplosion((int)body.Position.X, (int)body.Position.Y, (int)body.Position.Z, 40, true, false);
                }
            }
        }

        public ComponentMFTrain FindNearestTrain()
        {
            var bodies = new DynamicArray<ComponentBody>();
            m_systemBodies.FindBodiesAroundPoint(m_componentBody.Position.XZ, 2f, bodies);
            float num = 0f;
            ComponentMFTrain result = null;
            foreach (ComponentMFTrain train in bodies.Select(GetRailEntity))
            {
                if (train == null || train.Entity == Entity) continue;
                float score = 0f;
                const float maxDistance = 4f;
                if (train.m_componentBody.Velocity.LengthSquared() < 1f && train.Direction == Direction)
                {
                    var v = train.m_componentBody.Position + Vector3.Transform(train.m_componentMount.MountOffset, train.m_componentBody.Rotation) - m_componentBody.Position;
                    if (v.LengthSquared() <= maxDistance)
                        score = maxDistance - v.LengthSquared();
                }
                if (score > num)
                {
                    num = score;
                    result = train;
                }
            }
            return result;
        }
        public ComponentPlayer FindInteractingPlayer()
        {
            ComponentPlayer componentPlayer = Entity.FindComponent<ComponentPlayer>();
            if (componentPlayer == null)
            {
                ComponentBlockEntity componentBlockEntity = Entity.FindComponent<ComponentBlockEntity>();
                if (componentBlockEntity != null)
                {
                    var position = new Vector3(componentBlockEntity.Coordinates);
                    componentPlayer = Project.FindSubsystem<SubsystemPlayers>(throwOnError: true).FindNearestPlayer(position);
                }
            }
            return componentPlayer;
        }
        public ComponentPlayer FindNearestPlayer()
        {
            var position = new Vector3(m_componentBody.Position.X, m_componentBody.Position.Y, m_componentBody.Position.Z);
            return Project.FindSubsystem<SubsystemPlayers>(throwOnError: true).FindNearestPlayer(position);
        }
        public ComponentPlayer FindRidingPlayer()
        {
            return m_componentMount.Rider.Entity.FindComponent<ComponentPlayer>();
        }

        public void SetDirection(int value)
        {
            Direction = value;
            m_componentBody.Rotation = rotation;
        }

        public void Update(float dt)
        {
            if (componentEngine != null)
            {
                componentEngine.Coordinates = new Point3((int)m_componentBody.Position.X, (int)m_componentBody.Position.Y, (int)m_componentBody.Position.Z);
                componentEngine.Position = m_componentBody.Position;
            }

            if (m_componentMount.Rider != null)//坐火车时执行下面逻辑
            {
                var player = FindRidingPlayer();
                componentEngine.SetRidingPlayer(player);//设置正在骑乘的玩家
                player.ComponentLocomotion.LookOrder = player.ComponentInput.PlayerInput.Look;

                if (player.ComponentGui.m_clothingButtonWidget.IsChecked)
                {
                    //打开了背包界面，自动换成蒸汽机界面
                    if (player.ComponentGui.ModalPanelWidget is ClothingWidget)
                    {
                        player.ComponentGui.ModalPanelWidget = new MFEngineWidget(player.ComponentMiner.Inventory, componentEngine, componentChestCache);
                    }
                }
            }
            else componentEngine.SetRidingPlayer(null);//告诉引擎没有正在骑乘的玩家

            switch (Direction)
            {
                case 0:
                case 2:
                    m_componentBody.Position = new Vector3(MathUtils.Floor(m_componentBody.Position.X) + 0.5f, m_componentBody.Position.Y, m_componentBody.Position.Z);
                    break;
                case 1:
                case 3:
                    m_componentBody.Position = new Vector3(m_componentBody.Position.X, m_componentBody.Position.Y, MathUtils.Floor(m_componentBody.Position.Z) + 0.5f);
                    break;
            }
            ComponentMFTrain t = this;
            #region 车厢
            //ZS
            //int level = 0;
            //for (; t.ParentBody != null; level++) t = t.ParentBody;
            //if (level > 0)
            //{
            //    var body = t.m_componentBody;
            //    var pos = body.Position;
            //    var r = body.Rotation;
            //    //if (ParentBody!=null)
            //    //m_componentBody.Rotation = Quaternion.Slerp(m_componentBody.Rotation, rotation, 0.715f);
            //    var Com = body.Entity.FindComponent<ComponentEngine>();
            //    var Com2 = body.Entity.FindComponent<ComponentEngineE>();
            //    float HLD = 0f;
            //    if (Com != null)
            //        HLD = Com.HeatLevel >= 100f ? 100f : 1f;
            //    if (Com2 != null)
            //    {
            //        var result = Utils.SubsystemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0, 3f, 0), false, true, null) ?? default;
            //        if ((ElementBlock.Block.GetDevice(result.CellFace.X, result.CellFace.Y, result.CellFace.Z, result.Value) is TElectricWire em))
            //        {
            //            HLD = Com2.Charged ? 200f : 1f;
            //        }
            //        //HLD = Com2.Charged ? 200f : 1f;
            //    }
            //    //HLD = Com2.Charged ? 200f : 1f;
            //    if (m_componentBody.StandingOnValue.HasValue && HLD >= 100f && body.m_velocity.Length() > 0f) //body.Velocity.LengthSquared() > 10f &&
            //                                                                                                  //Utils.SubsystemTime.QueueGameTimeDelayedExecution(Utils.SubsystemTime.GameTime + 0.23 * level, delegate
            //    {
            //        var result = Utils.SubsystemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0, -3f, 0), false, true, null);
            //        //result.Value.CellFace.Point
            //        if (result.HasValue && Terrain.ExtractContents(result.Value.Value) == RailBlock.Index && (dt *= SimulateRail(RailBlock.GetRailType(Terrain.ExtractData(result.Value.Value)))) > 0f)
            //        {
            //            //if (rotation.ToForwardVector().Y != 0 || ParentBody.m_componentBody.Position.Y != m_componentBody.Position.Y)
            //            //{
            //            //	m_componentBody.m_velocity += dt2 * rotation.ToForwardVector();
            //            //	m_componentBody.Rotation = Quaternion.Slerp(m_componentBody.Rotation, rotation, 0.15f);
            //            //	return;
            //            //}
            //            float ABS = 6f;
            //            if (HLD >= 200f)
            //                ABS = 30f;
            //            //	if (rotation.ToForwardVector().Y != 0f) { ABS = 3f; };
            //            m_componentBody.m_velocity = ABS * rotation.ToForwardVector();
            //            //m_componentBody.m_velocity.Z = ABS * rotation.ToForwardVector().Z;
            //            // m_componentBody.m_velocity.Y += rotation.ToForwardVector().Y * dt2/2;
            //            if (ParentBody != null && MathUtils.Abs(Vector3.Distance(ParentBody.m_componentBody.Position, m_componentBody.Position)) > 2f)
            //            {
            //                m_componentBody.m_velocity = ABS * rotation.ToForwardVector() * 1.4f;
            //                //m_componentBody.m_velocity.Z = ABS * rotation.ToForwardVector().Z * 1.4f;
            //            }
            //            //m_componentBody.m_velocity = ParentBody.m_componentBody.m_velocity.Length() * rotation.ToForwardVector() * 1.2f;
            //            if (ParentBody != null && MathUtils.Abs(Vector3.Distance(ParentBody.m_componentBody.Position, m_componentBody.Position)) < 1.2f)
            //            {
            //                m_componentBody.m_velocity = ABS * rotation.ToForwardVector() * 0.3f;
            //                //m_componentBody.m_velocity.Z = ABS * rotation.ToForwardVector().Z * 0.6f;
            //            }
            //            if (ParentBody != null && MathUtils.Abs(Vector3.Distance(ParentBody.m_componentBody.Position, m_componentBody.Position)) < 0.8f)
            //            {
            //                m_componentBody.m_velocity = ABS * rotation.ToForwardVector() * 0f;
            //                //m_componentBody.m_velocity.Z = ABS * rotation.ToForwardVector().Z * 0.6f;
            //            }
            //            m_componentBody.Rotation = Quaternion.Slerp(m_componentBody.Rotation, rotation, 0.715f);

            //            //m_componentBody.m_velocity = ParentBody.m_componentBody.m_velocity.Length() * rotation.ToForwardVector() * 0.8f;
            //        }
            //    }
            //    else if (Com != null && HLD < 100f && m_componentBody.m_velocity.Length() - 0.06f > 0f && m_componentBody.StandingOnValue.HasValue)
            //    {
            //        var result = Utils.SubsystemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0, -3f, 0), false, true, null) ?? default;
            //        SimulateRail(RailBlock.GetRailType(Terrain.ExtractData(result.Value)));
            //        m_componentBody.m_velocity = (m_componentBody.m_velocity.Length() - 0.06f) * rotation.ToForwardVector();
            //    }
            //    if (!m_componentBody.StandingOnValue.HasValue)
            //    {
            //        //rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, 0);
            //        var result = Utils.SubsystemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0, -3f, 0), false, true, null) ?? default;
            //        SimulateRail(RailBlock.GetRailType(Terrain.ExtractData(result.Value)));
            //        m_componentBody.Rotation = rotation;
            //        m_componentBody.m_velocity -= new Vector3(0f, 40f, 0f) * dt;
            //        //m_componentBody.m_velocity -= new Vector3(0f,0.1f, 0f);
            //    }
            //    //m_componentBody.Rotation = Quaternion.Slerp(m_componentBody.Rotation, rotation, 0.715f);
            //    m_componentBody.Rotation = Quaternion.Slerp(m_componentBody.Rotation, rotation, 0.715f);
            //    m_outOfMountTime = MathUtils.Abs(Vector3.DistanceSquared(ParentBody.m_componentBody.Position, m_componentBody.Position)) > 24f
            //        ? m_outOfMountTime + dt
            //        : 0f;
            //    ComponentDamage ComponentDamage = ParentBody.Entity.FindComponent<ComponentDamage>();
            //    if (m_outOfMountTime > 12f || (componentDamage != null && componentDamage.Hitpoints <= .05f) || ComponentDamage != null && ComponentDamage.Hitpoints <= .05f)
            //        ParentBody = null;
            //    return;
            //}
            #endregion
            if (!m_componentBody.StandingOnValue.HasValue)
            {
                //rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, 0);
                var result = m_systemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0, -3f, 0), false, true, null) ?? default;
                SimulateRail(Terrain.ExtractData(result.Value));
                m_componentBody.Rotation = rotation;
                m_componentBody.m_velocity -= new Vector3(0f, 40f, 0f) * dt;
            }

            //ZS
            float force = 0f;
            if (componentEngine != null)
                force = componentEngine.Work;

            if (force >= 1 && m_componentBody.StandingOnValue.HasValue)
            {
                float ABS = 6f + 4 * (force - 1);
                var result = m_systemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0, -3f, 0), false, true, null) ?? default;
                if (Terrain.ExtractContents(result.Value) == MFRailBlock.Index && (dt *= SimulateRail(Terrain.ExtractData(result.Value))) > 0f)
                    m_componentBody.m_velocity = ABS * rotation.ToForwardVector2();//火车速度类
            }
            else if (force < 1 && m_componentBody.m_velocity.Length() - 0.06f > 0f && m_componentBody.StandingOnValue.HasValue)
            {
                var result = m_systemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0, -3f, 0), false, true, null) ?? default;
                SimulateRail(Terrain.ExtractData(result.Value));
                m_componentBody.m_velocity = (m_componentBody.m_velocity.Length() - 0.06f) * rotation.ToForwardVector2();
            }
            m_componentBody.Rotation = Quaternion.Slerp(m_componentBody.Rotation, rotation, 0.2f);
        }

        /// <summary>
        /// 模拟在铁轨上行驶？
        /// </summary>
        /// <param name="railType"></param>
        /// <returns></returns>
        private float SimulateRail(int data)
        {
            if(MFRailBlock.IsBreaker(data)) return 0f;
            int railType = MFRailBlock.GetRailType(data);
            if (MFRailBlock.IsCorner(railType))
            {
                if (GetOffsetOnDirection(m_componentBody.Position, m_forwardDirection) > 0.5f)
                    Turn(railType);
                return 50f;
            }
            if (MFRailBlock.IsDirectionX(railType) ^ !MFRailBlock.IsDirectionX(m_forwardDirection))
            {
                rotation = railType > 5
                    ? railType - 6 != Direction ? directions[Direction] * upwardDirection : directions[Direction] * downwardDirection
                    : directions[Direction];
                return railType > 5 && railType - 6 != Direction ? 30f : 50f;
            }
            return 0f;
        }

        /// <summary>
        /// 转向所调用的方法
        /// </summary>
        /// <param name="turnType"></param>
        /// <returns></returns>
        private bool Turn(int turnType)
        {
            if (Direction == turnType)
            {
                Direction = (Direction - 1) & 3;
                m_componentBody.Velocity = MathUtils.Abs(m_componentBody.Velocity.X + m_componentBody.Velocity.Z) * forwardVector;
                m_componentBody.Position = Vector3.Floor(m_componentBody.Position) + center;
                return true;
            }
            if (((Direction - 1) & 3) == turnType)
            {
                Direction = (Direction + 1) & 3;
                m_componentBody.Velocity = MathUtils.Abs(m_componentBody.Velocity.X + m_componentBody.Velocity.Z) * forwardVector;
                m_componentBody.Position = Vector3.Floor(m_componentBody.Position) + center;
                return true;
            }
            return false;
        }

        private static float GetOffsetOnDirection(Vector3 vec, int direction)
        {
            float offset = (direction & 1) == 0 ? vec.Z - MathUtils.Floor(vec.Z) : vec.X - MathUtils.Floor(vec.X);
            return (direction & 2) == 0 ? 1 - offset : offset;
        }

        public static ComponentMFTrain GetRailEntity(Component c) => c.Entity.FindComponent<ComponentMFTrain>();

        public void ThrowAllItems()
        {
            Vector3 position = m_componentBody.Position;
            foreach (IInventory item in Entity.FindComponents<IInventory>())
            {
                item.DropAllItems(position);
            }
        }
    }
}
